Skip to content

Switch to PostgreSQL 16 image#577

Open
ShimShtein wants to merge 4 commits into
theforeman:masterfrom
ShimShtein:test_pg16
Open

Switch to PostgreSQL 16 image#577
ShimShtein wants to merge 4 commits into
theforeman:masterfrom
ShimShtein:test_pg16

Conversation

@ShimShtein

Copy link
Copy Markdown
Member

Why are you introducing these changes? (Problem description, related links)

As part of being prepared to run on RHEL 10, we need to switch to postgres 16.

What are the changes introduced in this pull request?

  • Switch of the container to postgres 16 instead of 13.

How to test this pull request

If the system spins up successfully and works with content - it's good to go.

@ShimShtein ShimShtein marked this pull request as ready for review June 18, 2026 13:25
@ShimShtein

Copy link
Copy Markdown
Member Author

Extracted out of #395

@evgeni

evgeni commented Jun 18, 2026

Copy link
Copy Markdown
Member

Extracted out of #395

What for? (#395 already identified the problems with it)

@ShimShtein

Copy link
Copy Markdown
Member Author

Because #395 has both postgres and valkey in the same PR?

@ShimShtein

Copy link
Copy Markdown
Member Author

I see the tests are failing on password encryption algorithm detection: https://github.com/theforeman/foremanctl/actions/runs/27768839212/job/82162895170?pr=577#step:17:643
That's weird. Locally the same setup gives me:

[vagrant@quadlet ~]$ sudo podman exec postgresql psql -U postgres -c 'SHOW password_encryption'
 password_encryption 
---------------------
 scram-sha-256
(1 row)

Which is weird.

@evgeni evgeni force-pushed the test_pg16 branch 2 times, most recently from f9dc89d to f6f88a0 Compare June 22, 2026 09:40
@evgeni

evgeni commented Jun 22, 2026

Copy link
Copy Markdown
Member

I see the tests are failing on password encryption algorithm detection: https://github.com/theforeman/foremanctl/actions/runs/27768839212/job/82162895170?pr=577#step:17:643 That's weird. Locally the same setup gives me:

[vagrant@quadlet ~]$ sudo podman exec postgresql psql -U postgres -c 'SHOW password_encryption'
 password_encryption 
---------------------
 scram-sha-256
(1 row)

Which is weird.

It's not weird, as that was the ext DB and that wasn't using our image definition properly. Fixed.

ansible.builtin.include_role:
name: debug_tools

- name: 'Enable postgresql:16 module'

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@evgeni, let me ask this question again: wouldn't it be more resilient to run the PG commands from inside a PG container instead of installing the client tooling directly on the host?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would, sure. I just had none that I could integrate easily into the various workflows where we need Postgres tooling on the host.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'd likely want to write a module that does this to be re-used because there are postgres actions that are run in a lot more places. I am not sure I see the value of it at the moment but it might be worth exploring after we get everything else.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ehelms Debian 13 has PostgreSQL 17. That probably means you can't run pg_dump on the PostgreSQL 16 container we run, which breaks backups.

@evgeni

evgeni commented Jun 29, 2026

Copy link
Copy Markdown
Member

The upgrade complains:

Jun 28 14:37:27 quadlet.example.com postgresql[123049]: Incompatible data directory.  This container image provides
Jun 28 14:37:27 quadlet.example.com postgresql[123049]: PostgreSQL '16', but data directory is of
Jun 28 14:37:27 quadlet.example.com postgresql[123049]: version '13'.
Jun 28 14:37:27 quadlet.example.com postgresql[123049]: 
Jun 28 14:37:27 quadlet.example.com postgresql[123049]: This image supports automatic data directory upgrade from
Jun 28 14:37:27 quadlet.example.com postgresql[123049]: '15', please _carefully_ consult image documentation
Jun 28 14:37:27 quadlet.example.com postgresql[123049]: about how to use the '$POSTGRESQL_UPGRADE' startup option.

The fact its supporting 15→16 only is encoded here:
https://github.com/sclorg/postgresql-container/blob/f1c89bca212c6a829ab74fdfdeec264f0d08a992/16/Dockerfile.c10s#L13-L14

Funnily, the container actually contains 13 binaries:

% podman run --rm -ti quay.io/sclorg/postgresql-16-c10s bash
…
bash-5.2$ rpm -q postgresql-upgrade
postgresql-upgrade-16.13-2.el10.x86_64
bash-5.2$ rpm -qf /usr/lib64/pgsql/postgresql-13/bin/postgres
postgresql-upgrade-16.13-2.el10.x86_64
bash-5.2$ /usr/lib64/pgsql/postgresql-13/bin/postgres --version
postgres (PostgreSQL) 13.23

I'd argue this is a bug in the image we should ask for a fix.

- "../../../src/vars/defaults.yml"
- "../../../src/vars/flavors/{{ flavor }}.yml"
- "../../../src/vars/database.yml"
- "../../../src/vars/images.yml"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed now?

Comment thread src/roles/postgresql/tasks/main.yml Outdated
notify:
- Restart postgresql

- name: Check if PG_VERSION file exists

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This set of upgrade tasks look good. Could they go in a dedicated task file?

Comment thread src/roles/postgresql/tasks/main.yml Outdated
ansible.builtin.systemd:
name: postgresql.service
masked: false
when:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this when pattern is repeated, you can put it in a block.

@ekohl ekohl changed the title Switch to postgres 16 image Switch to PostgreSQL 16 image Jul 3, 2026

@ekohl ekohl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be easier to unconditionally set POSTGRESQL_UPGRADE so it will always run when needed?

Thinking about the logging aspect: where do the logs of the upgrade go now? Can we somehow debug it when it fails? When it's part of the regular service then you'll have it the journal and sosreport will pick it up already.

Comment thread src/roles/postgresql/tasks/main.yml Outdated
POSTGRESQL_MAX_CONNECTIONS: "{{ postgresql_max_connections }}"
POSTGRESQL_SHARED_BUFFERS: "{{ postgresql_shared_buffers }}"
POSTGRESQL_EFFECTIVE_CACHE_SIZE: "{{ postgresql_effective_cache_size }}"
POSTGRESQL_PREV_VERSION: "{{ postgresql_version }}"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please link why this workaround is needed

Suggested change
POSTGRESQL_PREV_VERSION: "{{ postgresql_version }}"
# https://github.com/sclorg/postgresql-container/issues/661
POSTGRESQL_PREV_VERSION: "{{ postgresql_version }}"

Comment thread src/roles/postgresql/tasks/main.yml Outdated
POSTGRESQL_SHARED_BUFFERS: "{{ postgresql_shared_buffers }}"
POSTGRESQL_EFFECTIVE_CACHE_SIZE: "{{ postgresql_effective_cache_size }}"
POSTGRESQL_PREV_VERSION: "{{ postgresql_version }}"
POSTGRESQL_UPGRADE: "hardlink"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please make this a variable so we could easily switch to copy?

Comment thread src/roles/postgresql/tasks/main.yml Outdated
- "postgresql_version == '13'"
register: postgresql_upgrade_result

- name: Stop PostgreSQL systemd service (Quadlet)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it needed to stop the service again?

@ShimShtein

Copy link
Copy Markdown
Member Author

Wouldn't it be easier to unconditionally set POSTGRESQL_UPGRADE so it will always run when needed?

This will throw a warning: https://github.com/sclorg/postgresql-container/blob/134bb39ab5c435087487c3422522bddf160f727d/16/root/usr/share/container-scripts/postgresql/common.sh#L415 that will be visible in the logs (and if I am not mistaken it exits immediately, but at this point I am not sure if my environment was clean when I tested it). That was my original thought, but I had to work around the message.

Thinking about the logging aspect: where do the logs of the upgrade go now? Can we somehow debug it when it fails? When it's part of the regular service then you'll have it the journal and sosreport will pick it up already.

Had to debug the process quite a lot. You will have limited logging in the playbook itself (I will remove the debug log level from the playbook), but the more interesting log will be in the data folder {{ postgresql_data_dir }}/userdata/log/ and {{ postgresql_data_dir }}/userdata-new/log/ depending on the failure stage. We do not collect these files explicitly in the sosreport, it is a bit tricky - I would say that it should be part of the postgresql plugin, since it's a standard postgresql action. On the other hand, the files are created there due to our specific postgresql configuration, so we can argue that we should collect this data. Let's create an issue and track postgresql upgrade logs separately.

Comment thread src/roles/postgresql/tasks/upgrade.yaml
@ShimShtein

Copy link
Copy Markdown
Member Author

packit error seems unrelated:

2026-07-06 12:17:47.459 logging.py        INFO   HTTP ERROR response 429  [https://raw.githubusercontent.com/theforeman/foreman-packaging/rpm/develop/packages/foreman/foremanctl/foremanctl.spec]

@ekohl ekohl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is not blocking, but something to consider. Possibly in a follow up.

containers.podman.podman_container:
name: "{{ postgresql_container_name }}-upgrade"
image: postgresql.image
state: quadlet

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it need to be a quadlet? In favor of it means you get logs in the journal, but it's also fragile: if Ansible somehow starts, it can leave the system in an unpredictable state.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if Ansible somehow starts, it can leave the system in an unpredictable state.

I don't follow this logic. However, I assume you are asking the question of a quadlet which generates a systemd service vs. just running the container directly with the equivalent of a podman run?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it need to be a quadlet?

I had the same question. I decided to go on with a quadlet to reuse the postgres.image container. If I spin it up as a separate container, it means I cannot reuse the image definition and any drop-ins that will be defined there. Originally it was a regular container.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I messed up: I intended to write "Ansible somehow stops in the middle". Not start.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it is disastrous:

  • if the playbook stops before defining the service - no harm done
  • if it stops after the service is defined, but before it executes the service - it will be executed at first reboot
  • if it stops after the execution - it may leave some leftovers, but they would be harmless

There are options to add the upgrade as a dependency for the postgres.container, so it will pull the service before the execution and then push the condition into the service startup, so it will just silently finish with noop in case the DB was already migrated.

I think it's too complex for a one time only step.

@ekohl ekohl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be OK with tracking the items in a follow up task when someone actually does proper testing of upgrades. Right now I consider this "good enough" to make CI pass.

quadlet_options:
- |
[Unit]
Description=PostgreSQL Database Upgrade from 13 to 16

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps make sure that systemd knows it's 1 or the other? Or will this introduce too much uncertainty?

Suggested change
Description=PostgreSQL Database Upgrade from 13 to 16
Description=PostgreSQL Database Upgrade from 13 to 16
Conflicts=postgresql.service

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to Gemini, the directive is symmetric, so it won't protect us from someone that tries to start the server. Actually it will just cause the upgrade container to stop (even more problematic in the middle of an upgrade).

Description=PostgreSQL Database Upgrade from 13 to 16
[Service]
Type=oneshot
RemainAfterExit=yes

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means the service will remain as active. Is that useful?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will mark the service as active(exited) when it will stop the execution. In the happy path it won't matter, since we remove the service completely, but if it fails, we will have indication whether it was run or not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants